/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package kuti.grp5;

import java.util.logging.Level;
import java.util.logging.Logger;
import kuti.*;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author Marc Schorderet <marc.schorderet@edu.hefr.ch>
 * @author Vincenzo Do <vincenzo.do@edu.hefr.ch>
 * @version 1.0
 *
 * @see IActuator
 */
public class Actuator implements IActuator {

    /*
     * public static final int NOTHING_CHANGED = 0; public static final int
     * MOVING_PARAMETER_CHANGED = 1; public static final int
     * CUTTER_STATE_PARAMETER_CHANGED = 2; public static final int
     * ORIENTATION_PARAMETER_CHANGED = 4; public static final int
     * RADAR_PARAMETER_CHANGED = 8;
     */
    private static final int CHAR_SIZE = 2;
    //private int changing = NOTHING_CHANGED;
    private EKutiState moveState = EKutiState.STOPPED;
    private ECutterState cutterState = ECutterState.OFF;
    private double relativeOrientation = 0.0;
    private boolean isColliding = false;
    private char[] message;
    private double traveledDistance;
    private IKuti kuti;
    private boolean waiting;
    private CyclicBarrier barrier;
    public long stepNr;
    private IDriver driver;
    private ISimulParams simParams;
    private boolean isRadarUpdated = false;
    private boolean isMovingUpdated = false;
    private boolean isRotateUpdated = false;
    private boolean isCutterStateUpdated = false;
    private boolean hasBrokenTheBarrier = false;

    public Actuator(IDriver driver, IKuti kuti, CyclicBarrier barrier) {
        this.driver = driver;
        this.kuti = kuti;
        this.barrier = barrier;
        this.waiting = true;
        this.stepNr = 0;
    }

    /**
     *
     * @param moveState State of the movement
     */
    @Override
    public void setMovingState(EKutiState moveState) {
        this.isMovingUpdated = true;
        this.moveState = moveState;
    }

    /**
     *
     * @param cutterState State of the cutter
     */
    @Override
    public void setCutterState(ECutterState cutterState) {
        this.isCutterStateUpdated = true;
        this.cutterState = cutterState;
    }

    /**
     *
     * @param relativeOrientation Relative orentation value
     */
    @Override
    public void changeOrientation(double relativeOrientation) {
        this.isRotateUpdated = true;
        this.relativeOrientation = relativeOrientation;
    }

    public double getOrientation() {
        return this.relativeOrientation;
    }

    @Override
    public void updateRadarData() {
        this.isRadarUpdated = true;
    }

    /**
     *
     * @return Returns true if the Kuti colliding, else return false.
     */
    @Override
    public boolean isColliding() {
        return isColliding;
    }

    @Override
    public double traveledDistance() {
        return traveledDistance;
    }

    public void setTraveledDistance(double traveledDistance) {
        this.traveledDistance = traveledDistance;
    }

    /**
     *
     * @param message Message to send on the other Kuti.
     */
    @Override
    public void setCollisionMessage(char[] message) {
        try {
            if (message.length * CHAR_SIZE <= simParams.getMessageLength()) {
                this.message = message;
            }
        } catch (NullPointerException e) {
            //If message has not been initialized
        }
    }

    /**
     *
     * @return Returns the collision message
     */
    @Override
    public char[] getCollisionMessage() {
        return message;
    }

    /**
     * Method that tells the WM that a kuti had decided what to do. if the
     * decisions comes after the DRIVER_TIME_LIMIT the kuti is deactivated.
     *
     * @return stepNr
     */
    @Override
    public long waitNextStep() {
        hasBrokenTheBarrier = true;
        stepNr++;
        isMovingUpdated = true;
        if (!barrier.isBroken()) {
            try {
                //Wait at the barrier
                barrier.await();
            } catch (InterruptedException ex) {
                Logger.getLogger(Actuator.class.getName()).log(Level.SEVERE, null, ex);
            } catch (BrokenBarrierException ex) {
                //this exception is normal, no handling needed.
            }
        }
        try {
            synchronized (this) {
                //wait that the world manager apply the actions
                if (waiting) {
                    wait();
                }
            }
        } catch (InterruptedException ex) {
            Logger.getLogger(Actuator.class.getName()).log(Level.SEVERE, null, ex);
        }

        resetBarrier();
        return stepNr;

    }

    /**
     * This method resets the variables for the next tick.
     */
    private synchronized void resetBarrier() {
        waiting = true;
    }

    public void setBarrier(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    /**
     * After the WM has applyied the actions of the kuti, it calls this method
     * to let the KUTI proceed.
     */
    public synchronized void nextStep() {
        waiting = false;
        this.notify();

    }

    public EKutiState getMovingState() {
        return moveState;
    }

    /**
     * Getter for the hasBrokenTheBarrier variable, withc tells if a KUTI is
     * arrived after the time limit
     *
     * @return hasBrokenTheBarrier
     */
    public boolean hasKutiBrokenTheBarrier() {
        return hasBrokenTheBarrier;
    }

    public ECutterState getCutterState() {
        return cutterState;
    }

    public void setColision(boolean b) {
        this.isColliding = b;
    }

    public void resetUpdate() {
        isRadarUpdated = false;
        isCutterStateUpdated = false;
        isMovingUpdated = false;
        isRotateUpdated = false;
    }

    public boolean isRadarUpdated() {
        return isRadarUpdated;
    }

    public boolean isCutterStateUpdated() {
        return isCutterStateUpdated;
    }

    public boolean isMovingUpdated() {
        return isMovingUpdated;
    }

    public boolean isRotateUpdated() {
        return isRotateUpdated;
    }
}
